/**
 *    Copyright 2012 Yohan Courbe
 *
 *  Licensed under the Apache License, Version 2.0 (the "License 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *      
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using NetworkAnalyzer.SQL;
using PacketDotNet;

namespace NetworkAnalyzer.Engines.UdpTransfertTracker
{
    public class UdpTransfertTrackerEngine : Engine
    {
        private readonly UdpTransfertList _transfertList = new UdpTransfertList();
        private readonly object _lock = new object();
        private readonly MysqlDatabaseConnection _dbConnection;
        private readonly Timer _decreaseTtlTimer = new Timer();
        private readonly Timer _updateDatabaseTimer = new Timer();

        public UdpTransfertTrackerEngine()
        {
            Console.WriteLine("UDP Transfert Tracker Engine started.");

            _dbConnection = MysqlDatabaseConnection.OpenConnection();

            _decreaseTtlTimer.Interval = 1000;
            _decreaseTtlTimer.AutoReset = true;
            _decreaseTtlTimer.Elapsed += DecreaseTtlTimerOnElapsed;
            _decreaseTtlTimer.Start();

            _updateDatabaseTimer.Interval = 10000;
            _updateDatabaseTimer.AutoReset = true;
            _updateDatabaseTimer.Elapsed += UpdateDatabaseTimerOnElapsed;
            _updateDatabaseTimer.Start();
        }

        private void UpdateDatabaseTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            UdpTransfertList udpTransfertCopy;

            lock (_lock)
            {
                udpTransfertCopy = new UdpTransfertList(_transfertList);    
            }

            _dbConnection.Query("START TRANSACTION;");

            foreach (UdpTransfert udpTransfert in udpTransfertCopy)
            {
                var query = String.Format("INSERT INTO `udp_transferts`(`localhost`, `localport`, `remotehost`, `remoteport`, `start_date`, `end_date`, `upload_bandwidth`, `download_bandwidth`, `is_terminated`, `applicative_protocol`, `number_of_packets`) VALUES ('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}', '{10}') ON DUPLICATE KEY UPDATE end_date = '{5}', upload_bandwidth = {6}, download_bandwidth = {7}, is_terminated = '{8}', applicative_protocol = '{9}', number_of_packets = '{10}'",
                    udpTransfert.Localhost.ToInt(),
                    udpTransfert.Localport,
                    udpTransfert.Remotehost.ToInt(),
                    udpTransfert.Remoteport,
                    udpTransfert.StartTime.GetMySqlFormattedDate(),
                    udpTransfert.EndTime.GetMySqlFormattedDate(),
                    udpTransfert.UploadBandiwdth,
                    udpTransfert.DownloadBandwidth,
                    udpTransfert.IsTerminated.GetMysqlBoolean(),
                    udpTransfert.ApplicativeProtocol,
                    udpTransfert.NumberOfPackets
                );

                _dbConnection.Query(query);

                if (udpTransfert.IsTerminated)
                {
                    udpTransfert.HasBeenSavedInDatabase = true;
                }
            }

            _dbConnection.Query("COMMIT;");

            lock (_lock)
            {
                var udpCollection = new List<UdpTransfert>(
                    _transfertList.Cast<UdpTransfert>().Where(
                        udpTransfert => udpTransfert.IsTerminated && udpTransfert.HasBeenSavedInDatabase));

                foreach (var udpTransfert in udpCollection)
                {
                    _transfertList.Remove(udpTransfert);
                }

                udpCollection.Clear();
            }
        }

        private void DecreaseTtlTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            lock (_lock)
            {
                foreach (UdpTransfert udpTransfert in _transfertList)
                {
                    udpTransfert.DecreaseTtl();
                }
            }
        }

        public override void AddIpUdpPacket(Packet packet, IpPacket ipPacket, UdpPacket udpPacket)
        {
            if (ipPacket.SourceAddress.IsLocalHost())
            {
                lock (_lock)
                {
                    _transfertList.GetUdpTransfert(ipPacket.SourceAddress, udpPacket.SourcePort, ipPacket.DestinationAddress,
                                              udpPacket.DestinationPort).AddPacket(packet, ipPacket, udpPacket);
                }
            }
            else if (ipPacket.DestinationAddress.IsLocalHost())
            {
                lock (_lock)
                {
                    _transfertList.GetUdpTransfert(ipPacket.DestinationAddress, udpPacket.DestinationPort,
                                              ipPacket.SourceAddress, udpPacket.SourcePort).AddPacket(packet, ipPacket,
                                                                                                      udpPacket);
                }
            }
        }

        public override void Start()
        {
            
        }

        public override void Stop()
        {
            
        }

        public override void AddPacket(Packet packet) { }
        public override void AddIpTcpPacket(Packet packet, IpPacket ipPacket, TcpPacket tcpPacket) { }
    }
}